#include <stdio.h>
#include <stdlib.h>
#include <vector>

#include "StatClient.h"
#include "StatAttn.h"
#include "formater.h"
#include "DtcStatAlarmReporter.h"
#include "stat_index.h"
//#include "daemon.h"
//#include "ttc_global.h"

const char progname[] = "stattool";
const char usage_argv[] = "dump Id or reporter";
char cacheFile[256] = "index.conf";
char tableFile[256] = "";
CStatClient stc;
std::vector<CStatClient::iterator> idset;
CTableFormater out;
int outfmt = CTableFormater::FORMAT_ALIGNED;
unsigned char outnoname;
unsigned char rawdata;
unsigned char alldata;
unsigned char nobase;


#if __WORDSIZE >= 64
#define F64	"%ld"
#else
#define F64	"%lld"
#endif

static inline void cell_id(unsigned int id)
{
	out.Cell("%u", id);
}

static inline void cell_sample_id(unsigned int id, unsigned int n)
{
	if(outnoname)
		out.Cell("%u.%u", id, n);
	else
		out.Cell("%u", id);
}

static inline void cell_name(const char *name)
{
	if(outnoname) return;
	if(outfmt == CTableFormater::FORMAT_ALIGNED)
		out.Cell("%s:", name);
	else
		out.Cell("%s", name);
}

static inline void cell_dummy(void)
{
	if(outfmt == CTableFormater::FORMAT_ALIGNED)
		out.Cell("-");
	else
#if GCC_MAJOR < 3
		out.Cell(" ");
#else
		out.Cell(NULL);
#endif
}

static inline void cell_base(int64_t v)
{
	if(outnoname) return;
	if(outfmt == CTableFormater::FORMAT_ALIGNED)
		out.Cell("count[>="F64"]:", v);
	else
		out.Cell("count[>="F64"]", v);
}

static inline void cell_nbase(int v)
{
	if(outfmt == CTableFormater::FORMAT_ALIGNED)
		out.Cell("[%d]", v);
	else
		out.Cell("%d", v);
}

static inline void cell_int(int64_t val)
{
	out.Cell(F64, val);
}

static inline void cell_fixed(int64_t val, int n, int div)
{
	const char *sign = "";
	if(val < 0)
	{
		val = - val;
		sign = "-";
	}
		
	out.Cell("%s"F64".%0*d", sign, val/div, n, (int)(val%div));
}

static inline void cell_percent(int64_t val)
{
	out.Cell(F64"%%", val);
}

static inline void cell_percent_fixed(int64_t val, int n, int div)
{
	const char *sign = "";
	if(val < 0)
	{
		val = - val;
		sign = "-";
	}
		
	out.Cell("%s"F64".%0*d%%", sign, val/div, n, (int)(val%div));
}

static inline void cell_hms(int64_t val)
{
	const char *sign = "";
	if(val < 0)
	{
		val = - val;
		sign = "-";
	}
		
	if(val < 60)
		out.Cell("%s%d", sign, (int)val);
	else if(val < 60*60)
		out.Cell("%s%d:%02d", sign, (int)(val/60), (int)(val%60));
	else
		out.Cell("%s"F64":%02d:%02d", sign, val/3600, (int)((val/60)%60), (int)(val%60));
}

static inline void cell_hmsmsec(int64_t val)
{
	const char *sign = "";
	if(val < 0)
	{
		val = - val;
		sign = "-";
	}
	if(val < 60*1000)
		out.Cell("%s%d.%03d", sign, (int)(val/1000), (int)(val%1000));
	else if(val < 60*60*1000)
		out.Cell("%s%d:%02d.%03d", sign, (int)(val/60000), (int)((val/1000)%60), (int)(val%1000));
	else
		out.Cell("%s"F64":%02d:%02d.%03d", sign,
				val/3600000, (int)((val/60000)%60), (int)((val/1000)%60), (int)(val%1000));
}

static inline void cell_hmsusec(int64_t val)
{
	const char *sign = "";
	if(val < 0)
	{
		val = - val;
		sign = "-";
	}
	out.Cell("%s"F64".%06d", sign, val/1000000, (int)(val%1000000));
}

static inline void cell_datetime(int64_t v)
{
	if(v==0) {
		out.CellV("-");
		return;
	}
	time_t t = v;
	struct tm tm;
	localtime_r(&t, &tm);
	out.CellV("%d-%d-%d %d:%02d:%02d",
		tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
		tm.tm_hour, tm.tm_min, tm.tm_sec);
}

static inline void cell_date(int64_t v)
{
	if(v==0) {
		out.CellV("-");
		return;
	}
	time_t t = v;
	struct tm tm;
	localtime_r(&t, &tm);
	out.CellV("%d-%d-%d",
		tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday);
}

static inline void cell_version(int64_t v)
{
	out.CellV("%d.%d.%d", (int)v/10000, (int)v/100%100, (int)v%100);
}

static inline void cell_bool(int64_t v)
{
        if(0 == v)
        {
                out.CellV("NO");
        }
        else
        {
                out.CellV("YES");
        }
}


inline void row_init(void)
{
	out.NewRow();
}

inline void row_clear(void)
{
	out.ClearRow();
}

inline void dump_table(void)
{
	out.Dump(stdout, outfmt);
}

void cell_value(int unit, int64_t val)
{
	if(rawdata) unit = SU_INT;
	switch(unit)
	{
	default:
	case SU_HIDE:
	case SU_INT: cell_int(val); break;
	case SU_INT_1: cell_fixed(val, 1, 10); break;
	case SU_INT_2: cell_fixed(val, 2, 100); break;
	case SU_INT_3: cell_fixed(val, 3, 1000); break;
	case SU_INT_4: cell_fixed(val, 4, 10000); break;
	case SU_INT_5: cell_fixed(val, 5, 100000); break;
	case SU_INT_6: cell_fixed(val, 6, 1000000); break;
	case SU_MSEC: cell_hmsmsec(val); break;
	case SU_USEC: cell_hmsusec(val); break;
	case SU_TIME: cell_hms(val); break;
	case SU_DATE: cell_date(val); break;
	case SU_DATETIME: cell_datetime(val); break;
	case SU_VERSION: cell_version(val); break;
        case SU_BOOL: cell_bool(val); break;
	case SU_PERCENT: cell_percent(val); break;
	case SU_PERCENT_1: cell_percent_fixed(val, 1, 10); break;
	case SU_PERCENT_2: cell_percent_fixed(val, 2, 100); break;
	case SU_PERCENT_3: cell_percent_fixed(val, 3, 1000); break;
	}
}

void dump_data(void)
{
	int64_t sc[16];
	int sn;

	unsigned int i;
	CStatClient::iterator s;

	stc.CheckPoint();
	//printf("dump data %d\n",idset.size());
	for(i = 0; i < idset.size(); i++)
	{
		s = idset[i];

		if(alldata==0 && s->unit() == SU_HIDE)
			continue;

		row_init();
		switch(s->type())
		{
		case SA_SAMPLE:
			cell_id(s->id());
			cell_name(s->name());
			//cell_value(s->unit(), stc.ReadSampleAverage(s, SC_CUR));
			cell_value(s->unit(), stc.ReadSampleAverage(s, SCC_10S));
			cell_value(s->unit(), stc.ReadSampleAverage(s, SCC_10M));
			cell_value(s->unit(), stc.ReadSampleAverage(s, SCC_ALL));

			if(nobase<2)
			{
				row_init();
				cell_sample_id(s->id(), 1);
				cell_name("count[all]");
				//cell_value(SU_INT, stc.ReadSampleCounter(s, SC_CUR));
				cell_value(SU_INT, stc.ReadSampleCounter(s, SCC_10S));
				cell_value(SU_INT, stc.ReadSampleCounter(s, SCC_10M));
				cell_value(SU_INT, stc.ReadSampleCounter(s, SCC_ALL));
			}
			if(nobase==0)
			{
				sn = stc.GetCountBase(s->id(), sc);
				for(int n=1; n<=sn; n++)
				{
					row_init();
					cell_sample_id(s->id(), n+1);
					cell_base(sc[n-1]);
					//cell_value(SU_INT, stc.ReadSampleCounter(s, SC_CUR, n));
					cell_value(SU_INT, stc.ReadSampleCounter(s, SCC_10S, n));
					cell_value(SU_INT, stc.ReadSampleCounter(s, SCC_10M, n));
					cell_value(SU_INT, stc.ReadSampleCounter(s, SCC_ALL, n));
				}
			}

			break;

		case SA_COUNT:
			cell_id(s->id());
			cell_name(s->name());
			//cell_value(s->unit(), stc.ReadCounterValue(s, SC_CUR));
			cell_value(s->unit(), stc.ReadCounterValue(s, SCC_10S));
			cell_value(s->unit(), stc.ReadCounterValue(s, SCC_10M));
			cell_value(s->unit(), stc.ReadCounterValue(s, SCC_ALL));
			break;
		case SA_VALUE:
			cell_id(s->id());
			cell_name(s->name());
			//cell_value(s->unit(), stc.ReadCounterValue(s, SC_CUR));
			cell_value(s->unit(), stc.ReadCounterValue(s, SCC_10S));
			cell_value(s->unit(), stc.ReadCounterValue(s, SCC_10M));
			cell_dummy();
			break;

		case SA_CONST:
			cell_id(s->id());
			cell_name(s->name());
			cell_value(s->unit(), stc.ReadCounterValue(s, SCC_10S));
			switch(s->unit())
			{
			case SU_DATETIME:
			case SU_DATE:
			case SU_VERSION:
                        case SU_BOOL:
				break;
			default:
				//cell_dummy();
				cell_dummy();
				cell_dummy();
				break;
			}
			break;

		case SA_EXPR:
			cell_id(s->id());
			cell_name(s->name());
			cell_dummy();
			cell_value(s->unit(), stc.ReadCounterValue(s, SCC_10S));
			cell_value(s->unit(), stc.ReadCounterValue(s, SCC_10M));
			cell_value(s->unit(), stc.ReadCounterValue(s, SCC_ALL));
			break;

		default:
			row_clear();
		}
	}
	dump_table();
}

void dump_base(void)
{
	int64_t sc[16];
	int sn;

	unsigned int i;
	CStatClient::iterator s;

	stc.CheckPoint();

	for(i = 0; i < idset.size(); i++)
	{
		s = idset[i];
		row_init();
		sn = stc.GetCountBase(s->id(), sc);
		cell_id(s->id());
		cell_name(s->name());
		cell_nbase(sn);
		for(int n=0; n<sn; n++)
			cell_value(s->unit(), sc[n]);
	}
	dump_table();
}

void create_files(const char *name)
{
	char buf[256];
	if(statmgr.CreateStatIndex(name, STATIDX, StatDefinition, buf, sizeof(buf)) < 0)
	{
		fprintf(stderr, "Fail to create stat index file: %s\n", buf);
		exit(-3);
	}
	fprintf(stderr, "stat index created: %s\n", STATIDX);
}

void init(const char *name)
{
	int ret;

	ret = stc.InitStatInfo(name, STATIDX);
	if(ret  < 0)
	{
		fprintf(stderr, "Cannot Initialize StatInfo: %s\n", stc.ErrorMessage());
		exit(-1);
	}
}

void parse_stat_id(int argc, char **argv)
{
	CStatClient::iterator n;

	if(argc==0)
	{
		for(n = stc.begin(); n != stc.end(); n++)
			idset.push_back(n);
		return;
	}

	for(; argc>0; argc--, argv++)
	{
		int s, e;
		switch(sscanf(argv[0], "%d-%d", &s, &e))
		{
		case 2:
			for(n = stc.begin(); n != stc.end(); n++)
			{
				if((int)n->id() >= s && (int)n->id() <= e)
					idset.push_back(n);
			}
			break;
		case 1:
			if((n = stc[s]) != NULL)
			{
				idset.push_back(n);
				break;
			}
			// fall through
		default:
			fprintf(stderr, "Invalid stat id [%s]\n", argv[0]);
			exit(-4);
		}
	}
}

void parse_sample_id(int argc, char **argv)
{
	CStatClient::iterator n;

	if(argc==0)
	{
		for(n = stc.begin(); n != stc.end(); n++)
			if(n->issample())
				idset.push_back(n);
		return;
	}

	for(; argc>0; argc--, argv++)
	{
		int s, e;
		switch(sscanf(argv[0], "%d-%d", &s, &e))
		{
		case 2:
			for(n = stc.begin(); n != stc.end(); n++)
			{
				if((int)n->id() >= s && (int)n->id() <= e && n->issample())
					idset.push_back(n);
			}
			break;
		case 1:
			if((n = stc[s]) != NULL && n->issample())
			{
				idset.push_back(n);
				break;
			}
			// fall through
		default:
			fprintf(stderr, "Invalid stat sample id [%s]\n", argv[0]);
			exit(-4);
		}
	}
}

void alter_base(int argc, char **argv)
{
	CStatClient::iterator n = NULL;
	int64_t sc[16];
	
	if(argc==0 || (n=stc[atoi(argv[0])])==NULL)
	{
		fprintf(stderr, "A stat sample id required\n");
		exit(-5);
	}
	argv++, argc--;
	if(argc > 16)
	{
		fprintf(stderr, "number of count base must <= 16\n");
		exit(-5);
	}
	for(int i=0; i<argc; i++)
		sc[i] = strtoll(argv[i], 0, 0);
	int ret = stc.SetCountBase(n->id(), sc, argc);
	if(ret < 0)
	{
		fprintf(stderr, "setbase failed for id: %d\n", n->id());
		exit(-5);
	}
	idset.push_back(n);
	dump_base();
}

void usage(void)
{
	fprintf(stderr,
		"Usage: stattool [-nct] cmd [args...]\n"
		"options list:\n"
		"	-a  output hidden id too\n"
		"	-r  output unformatted data\n"
		"	-n  Don't output stat name\n"
		"	-t  use tab seperated format\n"
		"	-c  use [,] seperated format\n"
		"command list:\n"
		"        create\n"
		"        dump name [id|id-id]...\n"
		"        getbase name [id|id-id]...\n"
		"        setbase name id v1 v2...\n"
	);
	exit(-2);
}

int main(int argc, char **argv)
{
	argv++, --argc;

	while(argc > 0 && argv[0][0]=='-')
	{
		const char *p = argv[0];
		char c;
		while((c=*++p))
		{
			switch(c)
			{
			case 'a':
				alldata = 1;
				break;
			case 'b':
				nobase++;
				break;
			case 'r':
				rawdata = 1;
				break;
			case 't':
				outfmt = CTableFormater::FORMAT_TABBED;
				break;
			case 'c':
				outfmt = CTableFormater::FORMAT_COMMA;
				break;
			case 'n':
				outnoname = 1;
				break;
			case 'h':
			case '?':
			default:
				fprintf(stderr, "Unknown options [%c]\n", c);
				usage();
			}
		}
		argv++, --argc;
	}

	if(argc <= 0)
		usage();
	else if(!strcasecmp(argv[0], "help"))
		usage();
	else if(!strcasecmp(argv[0], "create"))
	{
		if(argc <= 1)
			usage();
		create_files(argv[1]);
	}
	else if(!strcasecmp(argv[0], "dump"))
	{
		if(argc <= 1)
			usage();
		init(argv[1]);
		parse_stat_id(argc-=2, argv+=2);
		dump_data();
	}
	else if(!strcasecmp(argv[0], "getbase"))
	{
		if(argc <= 1)
			usage();
		init(argv[1]);
		parse_sample_id(argc-=2, argv+=2);
		dump_base();
	}
	else if(!strcasecmp(argv[0], "setbase"))
	{
		if(argc <= 1)
			usage();
		init(argv[1]);
		alter_base(argc-=2, argv+=2);
	}
	else if(!strcasecmp(argv[0], "reporter"))
	{
//		if(TTC_DaemonInit (argc, argv) < 0)
//		return -1;
		if(argc <= 2)
			usage();
		init(argv[1]);
		ALARM_REPORTER->SetStatClient(&stc);
		ALARM_REPORTER->SetTimeOut(5);
		RunReporter(stc, argv[2]?:"");
	} else
		usage();
	return 0;
}
